package awesome.cpu.structure;

import java.math.BigInteger;

import awesome.cpu.utils.Mode;

public class ALU extends NonSequentialComponent{
	//ALU Control code meaning :
	//0000 - add
	//0001 - minus
	//0010 - and
	//0011 - or
	//0100 - multiply
	
	private static boolean outputDebug = false;
	
	private final static ALU mInstance = new ALU();
	
	public static ALU getInstance(){
		return mInstance;
	}
	
	private ALU(){}
	
	public final static int KEY_INPUT1 = 1;
	private BigInteger mInput1;
	
	public final static int KEY_INPUT2 = 2;
	private BigInteger mInput2;
	
	public final static int KEY_ALU_RESULT_63_32 = 3;
	public final static int KEY_ALU_RESULT_31_0 = 4;
	private BigInteger mAluResult;
	private int mAluResultHigh;
	private int mAluResultLow; 
	
	public final static int KEY_N = 5;
	private int mSignN;
	public final static int KEY_Z = 6;
	private int mSignZ;
	public final static int KEY_C = 7;
	private int mSignC;
	public final static int KEY_V = 8;
	private int mSignV;
	
	public final static int KEY_CONTROL_INPUT = 9;
	private int mControlInput;
	
	@Override
	int getData(int keyInterface) {

		BusMonitor.transferData(Control.getInstance(), Control.KEY_ALU_OP,
				this, KEY_CONTROL_INPUT);
		BusMonitor.transferData(MuxA.getInstance(), MuxA.KEY_SELECTED, 
				this, KEY_INPUT1);
		BusMonitor.transferData(MuxB.getInstance(), MuxB.KEY_SELECTED, 
				this, KEY_INPUT2);

		calculate();
		
		if (outputDebug && Mode.getDebugLevel() >= Mode.DEBUG_LEVEL_PRINT_ALL) {
			System.out.println("ALU: ALUControlInput = " +mControlInput);
			System.out.println("ALU: Input1 = "+mInput1);
			System.out.println("ALU: Input2 = "+mInput2);
			System.out.println("ALU: ALUResult = "+mAluResult);
			System.out.println("ALU: ALUResultLo = "+mAluResultLow);
			System.out.println("ALU: ALUResultHi = "+mAluResultHigh);
		}
		
		if (keyInterface == KEY_ALU_RESULT_63_32){
			return mAluResultHigh;
		}else if (keyInterface == KEY_ALU_RESULT_31_0){
			return mAluResultLow;
		}else if (keyInterface == KEY_N){
			return mSignN;
		}else if (keyInterface == KEY_Z){
			return mSignZ;
		}else if (keyInterface == KEY_C){
			return mSignC;
		}else if (keyInterface == KEY_V){
			return mSignV;
		}
		return 0;
	}
	
	@Override
	boolean setData(int keyInterface, int data) {
		if (keyInterface == KEY_INPUT1){
			mInput1 = new BigInteger(String.valueOf(data));
			return true;
		}else if (keyInterface == KEY_INPUT2){
			mInput2 = new BigInteger(String.valueOf(data));
			return true;
		}else if (keyInterface == KEY_CONTROL_INPUT){
			mControlInput = data;
			return true;
		}else 
			return false;
	}
	
	void calculate(){
		
		switch(mControlInput){

		//controlInput = 0000: add
		case 0:
			mAluResult = mInput1.add(mInput2);
			break;
			
		//controlInput = 0001: minus
		case 1:
			mAluResult = mInput1.subtract(mInput2);
			break;
			
		//controlInput = 0010: and
		case 2:
			mAluResult = mInput1.and(mInput2);
			break;
			
		//controlInput = 0011: or
		case 3:
			mAluResult = mInput1.or(mInput2);
			break;
		
		//controlInput = 0100: multiply
		case 4:
			mAluResult = mInput1.multiply(mInput2);
			break;
		
		}
		
		//calculate the value of low part and high part if the result
		
		mAluResultHigh = mAluResult.shiftRight(32).intValue();
		mAluResultLow = mAluResult.intValue(); 
		
		//N
		if (mAluResultLow < 0)
			mSignN = 1;
		else mSignN = 0;
		
		//Z
		if (mAluResult.equals(BigInteger.ZERO))
			mSignZ = 1;
		else mSignZ = 0;
		
		//C
		if (mControlInput == 2 && mAluResult.shiftRight(32).intValue() != 0)
			mSignC = 1;
		else mSignC = 0;
		
		//V
		BigInteger tmp = mInput1.add(mInput2.negate()).add(BigInteger.ONE);
		if (
				((mInput1.intValue() & 0x80000000) == 1 
						&& (mInput2.intValue() & 0x80000000) == 0 
						&& (tmp.intValue() & 0x80000000) == 0)
				||
				((mInput1.intValue() & 0x80000000) == 0 
						&& (mInput2.intValue() & 0x80000000) == 1 
						&& (tmp.intValue() & 0x80000000) == 1)
		)
			mSignV = 1;
		else mSignV = 0;
			
		
	}
}
